package tcp

import (
	"context"
	"encoding/json"
	"errors"
	"github.com/sirupsen/logrus"
	"hcy-api/lib/network"
	"hcy-api/structs/base"
	"hcy-api/structs/enum"
	"net"
	"sync"
	"time"
	"vap_host/global"
)

var ClientConn *Client

type Client struct {
	ClientId   string
	tcp        net.Conn
	inChan     chan base.ClientInstruct
	outChan    chan base.ClientResponse
	closeChan  chan byte
	mutex      sync.Mutex
	IsClosed   bool
	pipOutChan chan string
	pipErrChan chan error
	pipCancel  context.CancelFunc
	baseInfo   map[string]string
}

func InitConnection() {
	ClientConn = &Client{
		ClientId:   global.Conf.AppHost.UID,
		tcp:        nil,
		inChan:     make(chan base.ClientInstruct, 1000),
		outChan:    make(chan base.ClientResponse, 1000),
		closeChan:  make(chan byte, 1),
		IsClosed:   true,
		pipOutChan: make(chan string),
		pipErrChan: make(chan error),
		baseInfo:   make(map[string]string),
	}
	ClientConn.dial()
	go func() {
		ticker := time.NewTicker(time.Duration(global.Conf.Tcp.Interval) * time.Second)
		for range ticker.C {
			ClientConn.dial()
		}
	}()
	return
}

// dial 拨号，连接服务器
func (c *Client) dial() {
	defer c.mutex.Unlock()
	c.mutex.Lock()
	if c.IsClosed {
		for _, ser := range global.Conf.Tcp.Servers {
			con, err := net.Dial("tcp", ser)
			if err != nil {
				logrus.Infof("连接服务器【%s】失败:%v", ser, err)
				c.IsClosed = true
			} else {
				logrus.Infof("连接服务器【%s】成功", ser)
				c.tcp = con
				c.closeChan = make(chan byte, 1)
				c.IsClosed = false
				// 启动读信息的协程
				go c.readLoop()
				// 启动写协程
				go c.writeLoop()
				// 开启协程处理客户端消息
				go c.handMsg()
				// 举手
				c.sayHi()
				break
			}
		}
	} else {
		c.Ping()
	}
}

func (c *Client) handMsg() {
	for {
		in, err := c.ReaderMessage()
		if err != nil {
			return
		}
		switch in.Type {
		case enum.Ping:
			continue
		case enum.SayHi:
			c.sayHi()
		case enum.CloudApps:
			c.uploadApps(in)
		case enum.CreateRdpWinAccount:
			c.createWinRdpAccount(in)
		default:
			logrus.Errorf("未知指令:%v ", in)
		}
	}
}
func (c *Client) Ping() {
	_ = c.WriteMessage(base.ClientResponse{
		ResultType: enum.Ping,
	})
}

func (c *Client) ReaderMessage() (msg base.ClientInstruct, err error) {
	select {
	case msg = <-c.inChan:
	case <-c.closeChan:
		err = errors.New("connection is closed")
	}
	return
}

func (c *Client) WriteMessage(msg base.ClientResponse) (err error) {
	msg.ClientType = global.ClientType
	msg.DeviceId = global.Conf.AppHost.UID
	msg.ClientTime = time.Now().Unix()
	select {
	case c.outChan <- msg:
	case <-c.closeChan:
		err = errors.New("connection is closed")
	}
	return
}

func (c *Client) readLoop() {
	var (
		bs  []byte
		err error
	)
	for {
		if bs, err = network.Decode(c.tcp); err != nil {
			logrus.Errorf("编码错误：%s,已读数据为：%s", err, string(bs))
			goto ERR
		}
		inst := base.ClientInstruct{}
		err = json.Unmarshal(bs, &inst)
		if err != nil {
			logrus.Errorf("读取到不合法的消息：%+v", inst)
			goto ERR
		}
		select {
		case c.inChan <- inst:
		case <-c.closeChan:
			goto ERR
		}
	}
	// 发生错误时应该退出循环，否则协程无法正常回收
ERR:
	c.Close()
}

func (c *Client) writeLoop() {
	for {
		select {
		case msg := <-c.outChan:
			if !c.IsClosed {
				bs, err := network.Encode(msg)
				if err != nil {
					goto ERR
				}
				_, err = c.tcp.Write(bs)
				if err != nil {
					goto ERR
				}
			}
		case <-c.closeChan:
			goto ERR
		}
	}
ERR:
	c.Close()

}

func (c *Client) Close() {
	defer c.mutex.Unlock()
	c.mutex.Lock()
	// 关闭操作只能执行一次
	c.ClientId = ""
	if c.tcp != nil {
		_ = c.tcp.Close()
	}
	if !c.IsClosed {
		logrus.Debugf("客户端下线：%+v", c)
		close(c.closeChan)
		c.IsClosed = true
	}
}
